﻿using MathNet.Numerics.Statistics;
namespace star_questionnair.Services.impl;

public class CorrelationAnalysisService : ICorrelationAnalysisService
    {
        private readonly IQuestionService _questionService;
        private readonly IAnswerService _answerService;
        private readonly IOptionService _optionService;
        private readonly IResponseService _responseService;

        // Pearson 常量
        public static readonly string PEARSON_ID = "1001";
        // Spearman 常量
        public static readonly string SPEARMAN_ID = "1002";
        // Kendall 常量
        public static readonly string KENDALL_ID = "1003";
        private const double NOVALUE = double.NaN;

        public CorrelationAnalysisService(
            IQuestionService questionService,
            IAnswerService answerService,
            IOptionService optionService,
            IResponseService responseService)
        {
            _questionService = questionService;
            _answerService = answerService;
            _optionService = optionService;
            _responseService = responseService;
        }

        // 单选题与单选题的相关性分析
        public async Task<double> CorrelationOqTOqAsync(int questionId1, int questionId2, string methodId)
        {
            var xArray = await QuestionToDoubleAsync(questionId1);
            var yArray = await QuestionToDoubleAsync(questionId2);
            return Correlation(xArray, yArray, methodId);
        }

        // 单选题与选项(多选题、排序题、程度题)的相关性分析
        public async Task<double> CorrelationOqTOAsync(int questionId, int optionId, string methodId)
        {
            var xArray = await QuestionToDoubleAsync(questionId);
            var yArray = await OptionToDoubleAsync(optionId);
            return Correlation(xArray, yArray, methodId);
        }

        // 选项和选项的相关性分析
        public async Task<double> CorrelationOTOAsync(int optionId1, int optionId2, string methodId)
        {
            var xArray = await OptionToDoubleAsync(optionId1);
            var yArray = await OptionToDoubleAsync(optionId2);
            return Correlation(xArray, yArray, methodId);
        }

        // 初始化问题的 Double[]
        public async Task<double[]> QuestionToDoubleAsync(int questionId)
        {
            // 获取该问题的所有答案
            var answers = _answerService.GetAnswersByQuestionId(questionId);
            answers.Sort((a, b) => a.ResponseId.CompareTo(b.ResponseId));

            double[] array = new double[answers.Count];
            for (int i = 0; i < answers.Count; i++)
            {
                array[i] = double.Parse(answers[i].Content);
            }
            return array;
        }

        // 初始化选项的 Double[]
        public async Task<double[]> OptionToDoubleAsync(int optionId)
        {
            // 获取该选项对应的题
            var option = _optionService.GetById(optionId);
            var question = await _questionService.GetQuestionByIdAsync(option.QuestionId);

            var responses = await _responseService.GetAllResponsesByQuestionnaireIdAsync(question.QuestionnaireId);
            responses.Sort((a, b) => a.Id.CompareTo(b.Id));

            double[] array = new double[responses.Count];
            int i = 0;

            if (question.Type.Equals("MULTIPLE_CHOICE", StringComparison.OrdinalIgnoreCase))
            {
                // 多选题
                foreach (var response in responses)
                {
                    array[i] = 0;
                    var answers = await _answerService.GetAnswerByResponseAndQuestionAsync(response.Id, question.Id);
                    if (answers.Any(a => double.Parse(a.Content) == optionId))
                    {
                        array[i] = 1;
                    }
                    i++;
                }
            }
            else if (question.Type.Equals("LEVELQUESTION", StringComparison.OrdinalIgnoreCase))
            {
                // 程度题
                foreach (var response in responses)
                {
                    array[i] = 0;
        
                    // 获取当前 response 和 question 对应的所有答案
                    var answers = await _answerService.GetAnswerByResponseAndQuestionAsync(response.Id, question.Id);

                    // 检查是否有答案，并获取第一个答案的 Level 属性
                    var firstAnswer = answers?.FirstOrDefault();
                    if (firstAnswer != null)
                    {
                        array[i] = firstAnswer.Level;
                    }
        
                    i++;
                }
            }
            else if (question.Type.Equals("SORTQUESTION", StringComparison.OrdinalIgnoreCase))
            {
                // 排序题
                foreach (var response in responses)
                {
                    array[i] = 0;
                    var answer = await _answerService.GetAnswerByResponseAndQuestionAsync(response.Id, question.Id);
                    var firstAnswer = answer?.FirstOrDefault();
                    if (firstAnswer != null)
                    {
                        array[i] = int.Parse(firstAnswer.Text);
                    }
                    i++;
                }
            }
            return array;
        }

        // 相关性计算
        
        public double Correlation(double[] xArray, double[] yArray, string methodId)
        {
            // 检查两个数组长度是否相等
            if (xArray.Length != yArray.Length)
            {
                throw new ArgumentException("The length of both arrays must be the same.");
            }
            else if (xArray.Length < 2)
            {
                return double.NaN; // 如果数据点少于 2 个，则返回 NaN
            }

            // 根据 methodId 调用不同的相关性计算方法
            switch (methodId)
            {
                case "1001": // 皮尔逊相关系数
                    return MathNet.Numerics.Statistics.Correlation.Pearson(xArray, yArray);
                
                case "1002": // 斯皮尔曼等级相关系数
                    return MathNet.Numerics.Statistics.Correlation.Spearman(xArray, yArray);
                
                case "1003": // 肯德尔等级相关系数
                    return KendallTau(xArray, yArray);
                
                default:
                    throw new ArgumentException("Invalid methodId");
            }
        }
        public double KendallTau(double[] xArray, double[] yArray)
        {
            // 确保两个数组的长度相等
            if (xArray.Length != yArray.Length)
            {
                throw new ArgumentException("Arrays must have the same length.");
            }

            int n = xArray.Length;
            int concordantPairs = 0;
            int discordantPairs = 0;

            // 比较每一对数据点
            for (int i = 0; i < n; i++)
            {
                for (int j = i + 1; j < n; j++)
                {
                    // 一致对 (C)
                    if ((xArray[i] - xArray[j]) * (yArray[i] - yArray[j]) > 0)
                    {
                        concordantPairs++;
                    }
                    // 不一致对 (D)
                    else if ((xArray[i] - xArray[j]) * (yArray[i] - yArray[j]) < 0)
                    {
                        discordantPairs++;
                    }
                }
            }

            // 计算 Kendall Tau
            double tau = (concordantPairs - discordantPairs) / Math.Sqrt((concordantPairs + discordantPairs) * (concordantPairs + discordantPairs));
            return tau;
        }
    }